package com.xiezc;

import com.xiezc.model.Device;
import com.xiezc.model.PTypeEnum;
import com.xiezc.model.Packet;
import com.xiezc.model.SendProgress;
import lombok.*;
import lombok.extern.slf4j.Slf4j;

import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import static com.xiezc.Xutil.outTime;
import static com.xiezc.Xutil.selfDeviceId;

/**
 * @author wb-xzc291800
 * @date 2018/09/05 15:10
 */
@Slf4j
public class SendPacketService {

    private BlockingQueue<SendPacket> sendPacketBuf = new LinkedBlockingQueue<>();

    static SendPacketService sendPacketService;

    @Setter
    private SendService sendService;

    final Map<Long, Device> deviceMap;

    @Setter
    private RetPacketService retPacketService;

    private SendPacketService(Map<Long, Device> deviceMap) {

        this.deviceMap = deviceMap;
    }

    @Synchronized
    static SendPacketService single(Map<Long, Device> deviceMap) {

        if (sendPacketService == null) {
            sendPacketService = new SendPacketService(deviceMap);
        }
        log.info("SendPacketService 生成发送包服务初始化完成... ");
        return sendPacketService;
    }

    boolean add(SendPacket packet) {

        return sendPacketBuf.add(packet);
    }

    void start() {

        Thread thread = new Thread(() -> {
            while (true) {
                try {
                    SendPacket take = sendPacketBuf.poll(2000, TimeUnit.MILLISECONDS);
                    if (Objects.isNull(take)) {
                        if (SendService.heartBeat.get()) {
                            //组装心跳包
                            List<SendObj> sendObjs = creatHeartBeat();
                            sendService.addBuf(sendObjs);
                            SendService.heartBeat.set(false);
                        }
                        continue;
                    }
                    List<Device> devices = take.getDevices();
                    //每个设备发送一份
                    for (Device device : devices) {
                        //TODO
                        take.setToken(device.getToken());
                        sendService.addBuf(take, device);
                        if (take.getPtype() == PTypeEnum.Find || take.getPtype().isRet()) {
                            continue;
                        } else {
                            //放入重发队列
                            retPacketService.add(take, device);
                        }
                    }

                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
            }
        });

        thread.setDaemon(true);
        thread.start();
    }

    private List<SendObj> creatHeartBeat() {

        List<SendObj> list = new ArrayList<>();
        long timeMillis = System.currentTimeMillis();
        deviceMap.forEach((key, val) -> {
            if (val.getConneted()) {
                if (key == selfDeviceId) {
                    return;
                }

                //超时
                if (timeMillis - val.getTime() > outTime) {

                    ByteBuffer byteBuffer = ByteBuffer.allocate(Xutil.headLen);
                    byteBuffer.put(PTypeEnum.Heart.getVal());
                    byteBuffer.putLong(val.getId());
                    byteBuffer.putLong(val.getToken());
                    byteBuffer.putLong(Xutil.snowflake.generate());
                    byteBuffer.putLong(0L);
                    byteBuffer.putLong(1L);
                    byteBuffer.put(new byte[16]);

                    SendObj bufferObj = new SendObj();
                    byteBuffer.flip();
                    bufferObj.setByteBuffer(byteBuffer);
                    bufferObj.setInetSocketAddress(val.getAddress());
                    list.add(bufferObj);
                }
            }
        });
        return list;
    }

}

/**
 * @author wb-xzc291800
 * @date 2018/09/07 17:37
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
class SendPacket extends Packet {

    /**
     * 消息的接受者
     */
    List<Device> devices;

    SendProgress sendProgress;

    public boolean add(Device device) {

        return devices.add(device);
    }

    public boolean add(Collection<Device> devices) {

        return devices.addAll(devices);
    }

}
